/* Funky instruction set datatype contains the extension to the function
 * name which will be called (in local window[] scope) and the 'real'
 * opcode in case of substitutions, as well as max number of bytes that
 * the function takes in storage - this is important as some of the
 * opcodes (ie: JMP) assign absolute values to the program counter so
 * we can't blindly assume that we must bump the PC every time
 */
var instructionSet = new Array();

// we do a lot of these
function decode_address_arg(addr) {
	return(parseInt(peek(addr + 1)) + parseInt((peek(addr + 2) * 16)));
}

// Break from program to monitor
instructionSet[0] = {	"opcode": 0,
			"mnemonic": "BRK",
			"description": "Stop Execution",
			"bytes": 0 };
function execute_BRK(bytes) {
	set_run_mode(programState.READY);
	return(bytes);
}

// Load accumulator from location xx
instructionSet[1] = {	"opcode": 1,
			"mnemonic": "LDA",
			"description": "Load Accumulator from location",
			"bytes": 3 };
function execute_LDA(bytes) {
	var addr = decode_address_arg(get_pc());
	set_accumulator(peek(addr));
	set_zero_flag(get_accumulator());
	return(bytes);
}

// Store accumulator into location xx
instructionSet[2] = {	"opcode": 2,
			"mnemonic": "STA",
			"description": "Store Accumulator to location",
			"bytes": 3 };
function execute_STA(bytes) {
	var addr = decode_address_arg(get_pc());
	poke(addr, get_accumulator());
	return(bytes);
}

// Clear carry (to zero)
instructionSet[3] = {	"opcode": 3,
			"mnemonic": "CLC",
			"description": "Clear Carry flag",
			"bytes": 1 };
function execute_CLC(bytes) {
	set_carry_flag(0);
	return(bytes);
}

// Add contents of location xx to accumulator with carry
instructionSet[4] = {	"opcode": 4,
			"mnemonic": "ADC",
			"description": "Add value to Accumulator with Carry",
			"bytes": 3 };
function execute_ADC(bytes) {
	var addr = decode_address_arg(get_pc());
	adc_accumulator(peek(addr));
	return(bytes);
}

// Decrement the contents of location xx
instructionSet[5] = {	"opcode": 5,
			"mnemonic": "DEC",
			"description": "Decrement the value in location",
			"bytes": 3 };
function execute_DEC(bytes) {
	var addr = decode_address_arg(get_pc());
	var value = peek(addr);
	value--;
	set_zero_flag(value);
	if (value < 0) {
		value = 15;
	}
	poke(addr, value);
	return(bytes);
}

// Increment the contents of location xx
instructionSet[6] = {	"opcode": 6,
			"mnemonic": "INC",
			"description": "Increment the value in location",
			"bytes": 3 };
function execute_INC(bytes) {
	var addr = decode_address_arg(get_pc());
	var value = peek(addr);
	value++;
	set_carry_flag(value);
	value = value % 16;
	set_zero_flag(value);
	poke(addr, value);
	return(bytes);
}

// Move immediate value into accumulator
instructionSet[7] = {	"opcode": 7,
			"mnemonic": "MOV",
			"description": "Move value into Accumulator",
			"bytes": 2 };
function execute_MOV(bytes) {
	set_accumulator(peek(get_pc() + 1));
	set_zero_flag(get_accumulator());
	return(bytes);
}

// Jump to location xx
instructionSet[8] = {	"opcode": 8,
			"mnemonic": "JMP",
			"description": "Jump to location",
			"bytes": 3 };
function execute_JMP(bytes) {
	var addr = decode_address_arg(get_pc());
	return(addr - get_pc());
}

// Jump to location xx if previous result != 0 (zero flag clear)
instructionSet[9] = {	"opcode": 9,
			"mnemonic": "JNE",
			"description": "Jump to location if Not Equal",
			"bytes": 3 };
function execute_JNE(bytes) {
	if (0 == zeroFlag) {
		var addr = decode_address_arg(get_pc());
		bytes = addr - get_pc();
	}
	return(bytes);
}

// Poke the IO space directly
instructionSet[10] = {	"opcode": 10,
			"mnemonic": "OUT",
			"description": "Write Accumulator to Port",
			"bytes": 2 };
function execute_OUT(bytes) {
	var port = peek(get_pc() + 1);
	var value = get_accumulator();
	set_port(port, value);
	return(bytes);
}

// Peek the IO space directly
instructionSet[11] = {	"opcode": 11,
			"mnemonic": "INB",
			"description": "Read from Port into Accumulator",
			"bytes": 2 };
function execute_INB(bytes) {
	var port = peek(get_pc() + 1);
	var value = get_port(port);
	set_carry_flag(value);
	set_accumulator(value);
	set_zero_flag(get_accumulator());
	return(bytes);
}

// Set carry (to one)
instructionSet[12] = {	"opcode": 12,
			"mnemonic": "SEC",
			"description": "Set Carry flag",
			"bytes": 1 };
function execute_SEC(bytes) {
	set_carry_flag(16);
	return(bytes);
}

// Subtract contents of xx from accumulator with carry
instructionSet[13] = {	"opcode": 13,
			"mnemonic": "SBC",
			"description": "Subtract location from Accumulator with Carry",
			"bytes": 3 };
function execute_SBC(bytes) {
	var addr = decode_address_arg(get_pc());
	var value = peek(addr);
	set_accumulator(get_accumulator() - value);
	set_accumulator(get_accumulator() - get_carry_flag());
	if (get_accumulator() < 0) {
		set_accumulator(16 + get_accumulator());
	}
	set_zero_flag(get_accumulator());
	return(bytes);
}

//Jump to location xx if carry is clear
instructionSet[14] = {	"opcode": 14,
			"mnemonic": "JCC",
			"description": "Jump to location if Carry clear",
			"bytes": 3 };
function execute_JCC(bytes) {
	if (0 == get_carry_flag()) {
		var addr = decode_address_arg(get_pc());
		bytes = addr - get_pc();
	}
	return(bytes);
}

// Jump to location xx if carry is set
instructionSet[15] = {	"opcode": 15,
			"mnemonic": "JCS",
			"description": "Jump to location if Carry set",
			"bytes": 3 };
function execute_JCS(bytes) {
	if (0 != get_carry_flag()) {
		var addr = decode_address_arg(get_pc());
		bytes = addr - get_pc();
	}
	return(bytes);
}

// instructions that follow need to be swapped into the main set

// Jump to location xx if zero flag is set
instructionSet[16] = {	"opcode": 16,
			"mnemonic": "JEQ",
			"description": "Jump to location if equal",
			"bytes": 3 };
function execute_JEQ(bytes) {
	if (0 != get_zero_flag()) {
		var addr = decode_address_arg(get_pc());
		bytes = addr - get_pc();
	}
	return(bytes);
}

// Subtract one from the accumulator
instructionSet[17] = {	"opcode": 17,
			"mnemonic": "DCA",
			"description": "Decrement the Accumulator by 1",
			"bytes": 1 };
function execute_DCA(bytes) {
	set_accumulator(get_accumulator() - 1);
	set_zero_flag(get_accumulator());
	if (get_accumulator() < 0) {
		set_accumulator(16 + get_accumulator());
	}
	return(bytes);
}

// Add one to the accumulator
instructionSet[18] = {	"opcode": 18,
			"mnemonic": "INA",
			"description": "Increment the Accumulator by 1",
			"bytes": 1 };
function execute_INA(bytes) {
	var acc = get_accumulator();
	acc += 1;
	set_carry_flag(acc);
	set_accumulator(acc);
	set_zero_flag(get_accumulator());
	return(bytes);
}

// Compare the accumulator with immediate value (zero flag set if true, clear otherwise)
instructionSet[19] = {	"opcode": 19,
			"mnemonic": "TST",
			"description": "Compare Accumulator with value",
			"bytes": 2 };
function execute_TST(bytes) {
	var value = peek(get_pc() + 1);
	set_zero_flag(get_accumulator() - value);
	return(bytes);
}

// Load accumulator with the contents pointed to by address xx and xx+1
instructionSet[20] = {	"opcode": 20,
			"mnemonic": "LDI",
			"description": "Load Accumulator with indirect location",
			"bytes": 3 };
function execute_LDI(bytes) {
	var addr = decode_address_arg(get_pc());
	var addr2 = decode_address_arg(addr);
	set_accumulator(peek(addr2));
	set_zero_flag(get_accumulator());
	return(bytes);
}

// Store accumulator into the address pointed to by xx and xx+1
instructionSet[21] = {	"opcode": 21,
			"mnemonic": "STI",
			"description": "Store Accumulator to indirect location",
			"bytes": 3 };
function execute_STI(bytes) {
	var addr = decode_address_arg(get_pc());
	var addr2 = decode_address_arg(addr);
	set_value(addr2, get_accumulator());
	return(bytes);
}

// Shift the Accumulator left one bit
instructionSet[22] = {	"opcode": 22,
			"mnemonic": "SAL",
			"description": "Shift Accumulator left 1 bit",
			"bytes": 1 };
function execute_SAL(bytes) {
	var acc = get_accumulator();
	acc = acc << 1;
	set_carry_flag(acc);
	set_accumulator(acc);
	set_zero_flag(get_accumulator());
	return(bytes);
}

// Shift the Accumulator right one bit
instructionSet[23] = {	"opcode": 23,
			"mnemonic": "SAR",
			"description": "Shift Accumulator right 1 bit",
			"bytes": 1 };
function execute_SAR(bytes) {
	var acc = get_accumulator();
	acc = acc >> 1;
	set_accumulator(acc);
	set_zero_flag(get_accumulator());
	return(bytes);
}

// Rotate the Accumulator left one bit through the Carry flag
instructionSet[24] = {	"opcode": 24,
			"mnemonic": "RAL",
			"description": "Roll Accumulator left 1 bit through Carry",
			"bytes": 1 };
function execute_RAL(bytes) {
	var acc = get_accumulator();
	acc = acc << 1;
	acc = acc | carryFlag;
	set_carry_flag(acc);
	set_accumulator(acc);
	set_zero_flag(get_accumulator());
	return(bytes);
}

// Rotate the Accumulator right one bit through the Carry flag
instructionSet[25] = {	"opcode": 25,
			"mnemonic": "RAR",
			"description": "Roll Accumulator right 1 bit through Carry",
			"bytes": 1 };
function execute_RAR(bytes) {
	var acc = get_accumulator();
	acc = acc >> 1;
	acc = acc | (get_carry_flag() * 8);
	set_carry_flag(acc);
	set_accumulator(acc);
	set_zero_flag(get_accumulator());
	return(bytes);
}
